<?php

namespace C\S\GroupBuying;

use C\L\Service;
use C\M\CouponReceive;
use C\M\GroupBuyingOrder;
use C\M\GroupBuyingGoods;
use C\M\Job;
use C\M\User;

class Order extends Service
{
    protected function setModel()
    {
        $this->model = new \C\M\GroupBuyingOrder();
    }

    public function getStatusConfig()
    {
        return [
            'state' => [
                'processing' => '拼团中',
                'success'    => '拼团成功',
                'failure'    => '拼团失败',
            ]
        ];
    }

    public function confirmFakeOrder($goods, $user)
    {
        $otherInfo = [
            'is_generated' => true,
            'coupon_id'    => null,
            'user'         => $user,
        ];

        $order = new GroupBuyingOrder;

        $order->user_id               = 0;
        $order->group_buying_goods_id = $goods->id;
        $order->quantity              = 1;
        $order->payment_amount        = $goods->price;
        $order->other_info            = json_encode($otherInfo);

        if (!$order->save()) {
            throw new \Exception('创建团购订单失败');
        }

        // 更新库存
        if (!$goods->updateStock(-1)) {
            throw new \Exception('更新库存失败');
        }

        // 更新销量
        if (!$goods->updateSales(1)) {
            throw new \Exception('更新销量失败');
        }

        return true;
    }

    public function confirm($userId, $goodsId, $quantity, $couponId, $masterOrderId = null)
    {
        try {
            $lockKey = "uid:$userId:group_buying_order_confirm";

            if (!$this->di['s_user']->lock($lockKey, 5)) {
                throw new \Exception('服务器繁忙，请稍后再试。');
            }
            $this->di['db']->begin();


            $goods = $this->getGoods($goodsId, $userId, $quantity);

            $coupon = $this->getCoupon($couponId, $userId, $goods, $quantity);

            // 生成团购订单
            $otherInfo = [
              'coupon_id'       => $couponId,
              'master_order_id' => $masterOrderId,
            ];
            $paymentAmount = $goods->price * $quantity - ($coupon->coupons->coupon_value ?? 0);

            $order = new GroupBuyingOrder;

            $order->user_id               = $userId;
            $order->group_buying_goods_id = $goodsId;
            $order->quantity              = $quantity;
            $order->payment_amount        = $paymentAmount;
            $order->other_info = json_encode($otherInfo);

            if (!$order->save()) {
                throw new \Exception('创建团购订单失败');
            }

            // 更新库存
            if (!$goods->updateStock(-1)) {
                throw new \Exception('更新库存失败');
            }

            // 更新销量
            if (!$goods->updateSales(1)) {
                throw new \Exception('更新销量失败');
            }

            // 核销代金券
            if ($coupon && !$coupon->use()) {
                throw new \Exception('该代金券无法核销');
            }

            // 更新用户余额
            if (!$this->di['s_user']->updateBalance(
                $userId,
                -$order->payment_amount,
                'confirm_group_buying_order',
                "发起团购:{$order->sn}",
                $order->id,
            )) {
                throw new \Exception('更新用户余额失败');
            }

            // 生成收益明细
            if (!$this->di['s_itemaprlist']->dispatchCommissionFromOrder($order)) {
                throw new \Exception('生成收益明细失败');
            }

            // 发放奖励
            $this->dispatchRewards($userId, $order, $goods);

            // 添加复购记录
            $slaveGoodsId = $goods->getSlaveGoodsId();
            if (!$masterOrderId && $slaveGoodsId) {
                $this->addBuySlaveGoodsJobFromOrder($order->id, $slaveGoodsId);
            }

            $this->di['db']->commit();
            $this->di['s_user']->unlock($lockKey);

            return true;
        } catch (\Exception $e) {
            if ($this->di['db']->isUnderTransaction()) {
                $this->di['db']->rollback();
            }
            $this->di['s_user']->unlock($lockKey);

            throw $e;
        }
    }

    private function getGoods($goodsId, $userId, $quantity)
    {
        $goods = GroupBuyingGoods::findFirst($goodsId);
        if (!$goods) {
            throw new \Exception('未找到该团购商品');
        }

        if ($goods->status != 'on_sale') {
            throw new \Exception('商品未上架');
        }

        if ($goods->stock <= 0) {
            throw new \Exception('商品库存不足');
        }

        if (!$goods->canBuy($userId, $quantity)) {
            throw new \Exception('超出发起限制');
        }

        $otherInfo = json_decode($goods->other_info, true);
        $level = $this->di['s_level']->getLevel($userId);
        if ($level['credit'] < ($otherInfo['group_buying_limit_point'] ?? 0)) {
            throw new \Exception('未达到发起等级');
        }

        return $goods;
    }

    private function getCoupon($couponId, $userId, $goods, $quantity)
    {
        if (!$couponId) {
            return;
        }

        $amount = $quantity * $goods->price;

        $coupon = CouponReceive::findFirst($couponId);
        if (!$coupon) {
            throw new \Exception('未找到该代金券');
        }

        if (!$coupon->canUse($userId, $amount)) {
            throw new \Exception('该代金券不符合要求');
        }

        return $coupon;
    }

    /**
     * 发放奖励
     *
     * @param object $order
     * @param int $userId
     **/
    private function dispatchRewards($userId, $order, $goods)
    {
        $config = $this->di['s_config']->get('reward');

        $user = User::findById($userId);

        $superior = User::findById($user->t_uid);

        // 发起团购积分奖励
        $this->dispatchUserPointReward($config, $order, $user);

        // 发起团购红包奖励
        $this->dispatchUserLuckyBagReward($config, $order, $user);

        // 发起指定团购商品奖励
        $this->dispatchUserGoodsReward($goods, $order, $user);

        if (!$superior) {
            return;
        }

        // 发起团购奖励
        $this->dispatchSuperiorReward($config, $order, $user, $superior);

        // 发起团购红包奖励
        $this->dispatchSuperiorLuckyBagReward($config, $order, $user, $superior);

        // 发起指定团购商品奖励
        $this->dispatchSuperiorGoodsReward($goods, $order, $user, $superior);
    }

    /**
     * 发放发起团购积分奖励（用户）
     *
     * @param array $config
     * @param object $order
     * @param object $user
     * @return bool
     **/
    private function dispatchUserPointReward($config, $order, $user)
    {
        $amount = bcmul($order->amount / 100, $config['group_buying_confirm_order_user_credit'] ?? 0, 2);
        if ($amount <= 0) {
            return true;
        }

        $title = '发起团购:' . $order->sn;

        $stype = 'group_buying_credit';

        $result = $this->di['s_funds']->add($user->uid, $amount, 'credit', $stype, $title, $order->id, 'D');

        if (!$result) {
            throw new \Exception('发放发起团购积分奖励失败');
        }

        return true;
    }

    /**
     * 发放发起团购红包奖励（用户）
     *
     * @param array $config
     * @param object $order
     * @param object $user
     * @return bool
     **/
    private function dispatchUserLuckyBagReward($config, $order, $user)
    {
        $amount = $config['group_buying_confirm_order_user_lucky_bag_money'] ?? 0;
        if ($amount <= 0) {
            return true;
        }

        $title = '发起团购红包奖励:' . $order->sn;

        $stype = 'group_buying_confirm_order_user_lucky_bag';

        $result = $this->di['s_funds']->add($user->uid, $amount, 'money', $stype, $title, $order->id, 'D');

        if (!$result) {
            throw new \Exception('发放发��团购红包奖励失败');
        }

        return true;
    }

    /**
     * 发放发起指定团购商品奖励（用户）
     *
     * @param object $goods
     * @param object $order
     * @param object $user
     * @return bool
     **/
    private function dispatchUserGoodsReward($goods, $order, $user)
    {
        $otherInfo = json_decode($goods->other_info, true);

        $amount = $otherInfo['reward_user'] ?? 0;
        if ($amount <= 0) {
            return true;
        }

        $title = '发起指定团购商品奖励:' . $order->sn;

        $stype = 'group_buying_confirm_order_user_goods';

        $result = $this->di['s_funds']->add($user->uid, $amount, 'money', $stype, $title, $order->id, 'D');

        if (!$result) {
            throw new \Exception('发放发起指定团购商品奖励失败');
        }

        return true;
    }

    /**
     * 发放发起团购奖励（上级）
     *
     * @param array $config
     * @param object $order
     * @param object $user
     * @param object $superior
     * @return bool
     **/
    private function dispatchSuperiorReward($config, $order, $user, $superior)
    {
        $rate = ($config['group_buying_confirm_order_superior_money'] ?? 0) / 100;

        $amount = $order->amount * $rate;
        if ($amount <= 0) {
            return true;
        }

        $title = '下级 ' . $user->getMosaicTel() . ' 发起团购获得奖励';

        $stype = 'group_buying_confirm_order_superior_reward';

        $result = $this->di['s_funds']->add($superior->uid, $amount, 'money', $stype, $title, $order->id, 'D');

        if (!$result) {
            throw new \Exception('发放发起团购奖励（上级）失败');
        }

        return true;
    }

    /**
     * 发放发起红包奖励（上级）
     *
     * @param array $config
     * @param object $order
     * @param object $user
     * @param object $superior
     * @return bool
     **/
    private function dispatchSuperiorLuckyBagReward($config, $order, $user, $superior)
    {
        $amount = $config['group_buying_confirm_order_superior_lucky_bag_money'] ?? 0;
        if ($amount <= 0) {
            return true;
        }

        $title = '下级 ' . $user->getMosaicTel() . ' 发起团购获得红包奖励';

        $stype = 'group_buying_confirm_order_superior_lucky_bag';

        $result = $this->di['s_funds']->add($superior->uid, $amount, 'money', $stype, $title, $order->id, 'D');

        if (!$result) {
            throw new \Exception('发放发起红包奖励（上级）失败');
        }

        return true;
    }

    /**
     * 发放发起指定团购商品奖励（上级）
     *
     * @param object $goods
     * @param object $order
     * @param object $user
     * @return bool
     **/
    private function dispatchSuperiorGoodsReward($goods, $order, $user, $superior)
    {
        $otherInfo = json_decode($goods->other_info, true);

        $amount = $otherInfo['reward_superior'] ?? 0;
        if ($amount <= 0) {
            return true;
        }

        $title = '下级 ' . $user->getMosaicTel() . ' 发起指定团购商品奖励';

        $stype = 'group_buying_confirm_order_superior_goods';

        $result = $this->di['s_funds']->add($superior->uid, $amount, 'money', $stype, $title, $order->id, 'D');

        if (!$result) {
            throw new \Exception('发放发起指定团购商品奖励（上级）失败');
        }

        return true;
    }

    /**
     * 添加复购任务
     *
     * @param int    $userId
     * @param int $masterOrderId
     * @return bool
     **/
    private function addBuySlaveGoodsJobFromOrder($masterOrderId, $slaveGoodsId)
    {
        /**
         * 直接传递 order 对象进来，会无法保存。估计和模型重复 save 有关
         * 因此传递 orderId，重新获取 order 对象，进行业务操作
         **/
        $order = GroupBuyingOrder::findById($masterOrderId);
        $orderOtherInfo = json_decode($order->other_info, true);
        $orderOtherInfo['is_master_order'] = true;

        $order->other_info = json_encode($orderOtherInfo);
        if (!$order->save()) {
            throw new \Exception('更新订单信息失败');
        }

        $payload = [
            'user_id'         => $order->user_id,
            'master_order_id' => $order->id,
            'slave_goods_id'  => $slaveGoodsId
        ];

        $period = $orderOtherInfo['commission']['period'] ?? 0;
        // 保险起见延迟 2 分钟
        $scheduledAt = time() + $period * 86400 + 120;

        $job = new Job;
        $job->event   = 'buy_slave_goods';
        $job->payload = json_encode($payload);
        $job->scheduled_at = date('Y-m-d H:i:s', $scheduledAt);
        if (!$job->save()) {
            throw new \Exception('创建复购任务失败');
        }

        return true;
    }
}
